package org.micode.libs.webflux.helper;

import org.micode.common.domain.base.BaseUserCache;
import org.micode.common.domain.vo.ConvertFileVO;
import org.micode.common.domain.vo.FileVO;
import org.micode.common.file.FileResponseVO;
import org.micode.common.file.FileUploadVO;
import org.micode.common.file.TargetPathHelper;
import org.micode.common.file.UploadFileTypeEnum;
import org.micode.common.utils.FileExtUtils;
import lombok.extern.slf4j.Slf4j;
import org.springframework.core.io.ClassPathResource;
import org.springframework.core.io.Resource;
import org.springframework.core.io.buffer.DataBufferUtils;
import org.springframework.http.HttpHeaders;
import org.springframework.http.MediaType;
import org.springframework.http.ZeroCopyHttpOutputMessage;
import org.springframework.http.codec.multipart.FilePart;
import org.springframework.http.server.reactive.ServerHttpResponse;
import reactor.core.publisher.Mono;

import java.io.File;
import java.io.IOException;
import java.nio.channels.AsynchronousFileChannel;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.StandardOpenOption;
import java.util.function.Function;

@Slf4j
public class FileTransferHelper {

    public static FileResponseVO uploadFile(FilePart filePart,
                                            Integer userType,
                                            UploadFileTypeEnum fileType,
                                            String bizType,
                                            String langType,
                                            BaseUserCache currentUser,
                                            TargetPathHelper targetPathHelper,
                                            Function<FileUploadVO, Integer> doSaveFileInfo,
                                            Function<ConvertFileVO, Integer> doConvertFile) throws IOException {
        String filename = filePart.filename();
        log.info("=======================Upload File=======================");
        log.info(filename);

        Long fileId = targetPathHelper.getFileId();
        String newFilename = targetPathHelper.getNewFilename();
        Path targetPath = targetPathHelper.getTargetPath();

        String newRealName = targetPathHelper.getNewRealName();
        String newRealPath = targetPathHelper.getNewRealPath();

        // 再建立新的
        Path tempFile = Files.createFile(targetPath);

        // 方法一: 异步传输文件
        AsynchronousFileChannel channel = AsynchronousFileChannel.open(tempFile, StandardOpenOption.WRITE);
        DataBufferUtils.write(filePart.content(), channel, 0)
                .doOnComplete(() -> {
                    log.info("File written...{}", newFilename);
                    String filePath = targetPath.toString();
                    doSaveFileInfo.apply(
                            new FileUploadVO(fileId, userType, currentUser,
                                    newRealPath, filename, newRealName, fileType.getCode(),
                                    getRealFileSize(filePath),
                                    bizType, langType, null));
                    try {
                        // 不关闭的话如果再上传同一个文件，会报错：java.nio.file.AccessDeniedException，因为资源被占用，无法删除
                        channel.close();
                    } catch (IOException e) {
                        log.warn("File stream closing failed...");
                    }

                    log.debug("Start converting file format...{}", newFilename);
                    // 文件转换格式
                    doConvertFile.apply(new ConvertFileVO(filePath, newRealPath));
                })
                .subscribe();

//        // 方法二
//        filePart.transferTo(tempFile.toFile());

        log.info(targetPath.toString());
        log.info("======================================================");

        FileResponseVO response = new FileResponseVO();
        response.setFileId(fileId);
        response.setFilename(newFilename);
        response.setFileUrl(targetPath.toString());

        return response;
    }

    private static Long getRealFileSize(String filePath) {
        return FileExtUtils.getFileSize(filePath);
    }

    public static Mono<Void> downloadFile(FileVO fileVO,
                                          ServerHttpResponse response) {
        ZeroCopyHttpOutputMessage zeroCopyResponse = (ZeroCopyHttpOutputMessage) response;
        response.getHeaders().set(HttpHeaders.CONTENT_DISPOSITION, "attachment; filename=" + fileVO.getSourceName());
        response.getHeaders().setContentType(MediaType.ALL);

        Resource resource = new ClassPathResource(fileVO.getUrl());
        File file = null;
        try {
            file = resource.getFile();
        } catch (IOException e) {
            log.error("Failed to read file: {}", fileVO, e);
            return null;
        }

        return zeroCopyResponse.writeWith(file, 0, file.length());
    }

}
